package ourvod.rtp;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Panel;
import java.util.Vector;

import javax.media.ControllerErrorEvent;
import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.Player;
import javax.media.RealizeCompleteEvent;
import javax.media.control.BufferControl;
import javax.media.protocol.DataSource;
import javax.media.rtp.Participant;
import javax.media.rtp.RTPControl;
import javax.media.rtp.RTPManager;
import javax.media.rtp.ReceiveStream;
import javax.media.rtp.ReceiveStreamListener;
import javax.media.rtp.SessionListener;
import javax.media.rtp.event.ByeEvent;
import javax.media.rtp.event.NewParticipantEvent;
import javax.media.rtp.event.NewReceiveStreamEvent;
import javax.media.rtp.event.ReceiveStreamEvent;
import javax.media.rtp.event.RemotePayloadChangeEvent;
import javax.media.rtp.event.SessionEvent;
import javax.media.rtp.event.StreamMappedEvent;

import net.jxta.protocol.PipeAdvertisement;

/**
 * @author cuizhf
 * 这个类，用以接收RTPStream
 */
public class RTPJxtaMulticastReceiver implements ReceiveStreamListener, SessionListener, ControllerListener {

    // RTP管理器数组
	RTPManager mgrs[] = null;
    // 管理播放器窗口的向量
    Vector playerWindows = null;
    // 是否接收到数据的标志
    boolean dataReceived = false;
    // 同步对象
    Object dataSync = new Object();
    
    private PipeAdvertisement dataPipeAdv;
    private PipeAdvertisement ctrlPipeAdv;

    /**
     * 构造方法
     */
    public RTPJxtaMulticastReceiver(PipeAdvertisement dataPipeAdv, PipeAdvertisement ctrlPipeAdv) {
        this.dataPipeAdv = dataPipeAdv;
        this.ctrlPipeAdv = ctrlPipeAdv;
    }
    
    /**
     * 初始化RTP会话，准备接收数据
     */
    public boolean initialize() {

        try {
            // 构造一个向量数组管理多个播放窗口
            playerWindows = new Vector();

            // 为每一个RTP会话建立一个管理器 
            mgrs = new RTPManager[1];

            // Open the RTP sessions.
            // 对每一个RTP会话
            for (int i = 0; i < mgrs.length; i++) {

                // 为每一个RTP会话产生一个RTP管理器         
            	mgrs[i] = (RTPManager) RTPManager.newInstance();
                // 添加会话监听器     
            	mgrs[i].addSessionListener(this);
                // 添加接受到数据的监听器
            	mgrs[i].addReceiveStreamListener(this);
                
                mgrs[i].initialize(new RTPJxtaMulticastSocketAdapter(dataPipeAdv, ctrlPipeAdv));
                
                // You can try out some other buffer size to see
                // if you can get better smoothness.
                // 获得缓冲区控制         
                BufferControl bc = (BufferControl) mgrs[i].getControl("javax.media.control.BufferControl");
                if (bc != null) {
                    // 设置缓冲区大小（也可以使用其他值）        
                	bc.setBufferLength(BufferControl.MAX_VALUE);
                }

            }

        } catch (Exception e) {
        	e.printStackTrace();
            System.err.println("Cannot create the RTP Session: " + e.getMessage());
            return false;
        }

        // Wait for data to arrive before moving on.

        // 获取当前时间
        long then = System.currentTimeMillis();
        // 最多等待30秒
        long waitingPeriod = 30000;  // wait for a maximum of 30 secs.

        try {
            synchronized (dataSync) {
                while (!dataReceived
                        && System.currentTimeMillis() - then < waitingPeriod) {
                    if (!dataReceived) {
                        System.err.println("  - Waiting for RTP data to arrive");
                    }
                    dataSync.wait(1000);
                }
            }
        } catch (Exception e) {
        }

        // 在设定的时间内没有等到数据
        if (!dataReceived) {
            System.err.println("No RTP data was received.");
            close();
            return false;
        }

        return true;
    }

    /**
     * Close the players and the session managers.
     * 关闭播放器和会话管理器
     */
    protected void close() {

        for (int i = 0; i < playerWindows.size(); i++) {
            try {
                // 关闭播放窗口
                ((PlayerWindow) playerWindows.elementAt(i)).close();
            } catch (Exception e) {
            }
        }

        // 删除所有播放窗口
        playerWindows.removeAllElements();

        // close the RTP session.
        for (int i = 0; i < mgrs.length; i++) {
            if (mgrs[i] != null) {
                mgrs[i].removeTargets("Closing session from AVReceive3");
                mgrs[i].dispose();
                mgrs[i] = null;
            }
        }
    }

    /**
     * 判断数据是否接收完成
     */
    public boolean isDone() {
        return playerWindows.isEmpty();
    }

    /**
     * 通过播放器查找播放窗口
     */
    PlayerWindow find(Player p) {
        for (int i = 0; i < playerWindows.size(); i++) {
            PlayerWindow pw = (PlayerWindow) playerWindows.elementAt(i);
            if (pw.player == p) {
                return pw;
            }
        }
        return null;
    }

    /**
     * 通过接收数据流查找播放窗口
     */
    PlayerWindow find(ReceiveStream strm) {
        for (int i = 0; i < playerWindows.size(); i++) {
            PlayerWindow pw = (PlayerWindow) playerWindows.elementAt(i);
            if (pw.stream == strm) {
                return pw;
            }
        }
        return null;
    }

    /**
     * SessionListener.
     */
    @Override
    public synchronized void update(SessionEvent evt) {
        if (evt instanceof NewParticipantEvent) {
            Participant p = ((NewParticipantEvent) evt).getParticipant();
            System.err.println("  - A new participant had just joined: " + p.getCNAME());
        }
    }

    /**
     * ReceiveStreamListener
     */
    @Override
    public synchronized void update(ReceiveStreamEvent evt) {

        RTPManager mgr = (RTPManager) evt.getSource();
        // 获取加入者
        Participant participant = evt.getParticipant();    // could be null.
        // 获取接受到的数据流
        ReceiveStream stream = evt.getReceiveStream();  // could be null.

        if (evt instanceof RemotePayloadChangeEvent) {

            System.err.println("  - Received an RTP PayloadChangeEvent.");
            System.err.println("Sorry, cannot handle payload change.");
            System.exit(0);

        } else if (evt instanceof NewReceiveStreamEvent) {
            // 接受到新的数据流
            try {
                // 获取新数据流
                stream = ((NewReceiveStreamEvent) evt).getReceiveStream();
                // 获取数据源
                DataSource ds = stream.getDataSource();

                // Find out the formats.
                // 获取RTP控制
                RTPControl ctl = (RTPControl) ds.getControl("javax.media.rtp.RTPControl");
                if (ctl != null) {
                    System.err.println("  - Recevied new RTP stream: " + ctl.getFormat());
                } else {
                    System.err.println("  - Recevied new RTP stream");
                }

                if (participant == null) {
                    System.err.println("      The sender of this stream had yet to be identified.");
                } else {
                    System.err.println("      The stream comes from: " + participant.getCNAME());
                }

                // create a player by passing datasource to the Media Manager
                // 通过数据源构造一个媒体播放器
                Player p = javax.media.Manager.createPlayer(ds);
                if (p == null) {
                    return;
                }

                // 给播放器添加控制器监听        
                p.addControllerListener(this);
                // 实现播放器
                p.realize();
                // 通过播放器和数据流构造播放窗口      
                PlayerWindow pw = new PlayerWindow(p, stream);
                // 将该播放窗口加入向量数组中
                playerWindows.addElement(pw);

                // Notify intialize() that a new stream had arrived.
                // 表明已经接收到了一个新数据流
                synchronized (dataSync) {
                    dataReceived = true;
                    dataSync.notifyAll();   // 通知initialize()方法中的等待过程
                }

            } catch (Exception e) {
                System.err.println("NewReceiveStreamEvent exception " + e.getMessage());
                return;
            }

        } else if (evt instanceof StreamMappedEvent) {
            // 数据流映射事件 
        	if (stream != null && stream.getDataSource() != null) {
                DataSource ds = stream.getDataSource();
                // Find out the formats.
                RTPControl ctl = (RTPControl) ds.getControl("javax.media.rtp.RTPControl");
                System.err.println("  - The previously unidentified stream ");
                if (ctl != null) {
                    System.err.println("      " + ctl.getFormat());
                }
                System.err.println("      had now been identified as sent by: " + participant.getCNAME());
            }
        } else if (evt instanceof ByeEvent) {
            // 数据流接收完毕 
        	System.err.println("  - Got \"bye\" from: " + participant.getCNAME());
            PlayerWindow pw = find(stream);
            if (pw != null) {
                pw.close(); // 关闭播放窗口
                playerWindows.removeElement(pw);    // 从向量中去掉该播放窗口
            }
        }

    }

    /**
     * ControllerListener for the Players.
     */
    @Override
    public synchronized void controllerUpdate(ControllerEvent ce) {

        Player p = (Player) ce.getSourceController();

        if (p == null) {
            return;
        }

        // Get this when the internal players are realized.
        if (ce instanceof RealizeCompleteEvent) {
            // 播放器实现完成 
        	PlayerWindow pw = find(p);
            if (pw == null) {
                // Some strange happened.
                // 出现了错误
            	System.err.println("Internal error!");
                System.exit(-1);
            }
            pw.initialize();        // 初始化播放窗口
            pw.setVisible(true);    // 显示播放窗口
            p.start();              // 开始播放
        }

        if (ce instanceof ControllerErrorEvent) {
            // 控制器错误
        	p.removeControllerListener(this);
            PlayerWindow pw = find(p);
            if (pw != null) {
                pw.close();
                playerWindows.removeElement(pw);
            }
            System.err.println("AVReceive3 internal error: " + ce);
        }

    }


    /**
     * GUI classes for the Player.
     * 播放窗口类
     */
    class PlayerWindow extends Frame {
        // 播放器对象
        Player player;
        // 接收数据流对象
        ReceiveStream stream;

        /**
         * 构造方法
         */
        PlayerWindow(Player p, ReceiveStream strm) {
            player = p;
            stream = strm;
        }

        /**
         * 初始化
         */
        public void initialize() {
            add(new PlayerPanel(player));
        }

        public void close() {
            player.close();
            setVisible(false);
            dispose();
        }

        /**
         * 通知消息
         */
        @Override
        public void addNotify() {
            super.addNotify();
            pack();
        }
    }

    /**
     * GUI classes for the Player.
     * 播放器组件类
     */
    class PlayerPanel extends Panel {

        Component vc, cc;

        /**
         * 构造方法
         */
        PlayerPanel(Player p) {
            setLayout(new BorderLayout());
            if ((vc = p.getVisualComponent()) != null) {
                // 添加播放器视频组件
            	add("Center", vc);
            }
            if ((cc = p.getControlPanelComponent()) != null) {
                // 添加播放器控制组件         
            	add("South", cc);
            }
        }

        @Override
        public Dimension getPreferredSize() {
            int w = 0, h = 0;
            if (vc != null) {
                Dimension size = vc.getPreferredSize();
                w = size.width;
                h = size.height;
            }
            if (cc != null) {
                Dimension size = cc.getPreferredSize();
                if (w == 0) {
                    w = size.width;
                }
                h += size.height;
            }
            if (w < 160) {
                w = 160;
            }
            return new Dimension(w, h);
        }
    }
}
